package org.zebra.logging.handle;

import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.support.AopUtils;
import org.springframework.context.expression.AnnotatedElementKey;
import org.springframework.expression.EvaluationContext;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 对日志进行SPEL处理
 *
 * @author xub
 * @since 2023/9/1 上午9:22
 */
@Slf4j
public class LogSpelProcess {

    private static final Pattern PATTERN_ATTRIBUTE = Pattern.compile("\\#\\{(.*?)}");

    private final OperateLogExpressionEvaluator cachedExpressionEvaluator = new OperateLogExpressionEvaluator();

    /**
     * key 为字段未SPEL解析属性, value为已解析的属性，如果解析失败依旧是未解析属性
     * <p>
     * 这里巧妙在 spelValues.put(tpl, tpl);后期如果解析失败那获取的是未解析成功或者不需要解析的属性
     *
     * @param templates 需要解析的属性
     * @param args
     * @param method
     * @param target
     * @return 已经解析过的map
     */
    public HashMap<String, String> processBeforeExec(
            List<String> templates, Method method, Object[] args, Object target) {
        // 获取切点方法上的注解
        //        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        // Method method = methodSignature.getMethod();
        // Object[] args = joinPoint.getArgs();
        // Object target = joinPoint.getTarget();
        Class<?> targetClass = AopUtils.getTargetClass(target);
        HashMap<String, String> map = new HashMap<>();
        AnnotatedElementKey elementKey = new AnnotatedElementKey(method, targetClass);
        EvaluationContext evaluationContext =
                cachedExpressionEvaluator.createEvaluationContext(targetClass, method, args);
        for (String template : templates) {
            map.put(template, template);
            // 一定要先处理完方法 在去处理属性
            String str = map.get(template);
            if (str.contains("{")) {
                Matcher matcher = PATTERN_ATTRIBUTE.matcher(str);
                StringBuffer bufferStr = new StringBuffer();
                while (matcher.find()) {
                    String paramName = matcher.group(1);
                    if (!paramName.contains("#")) {
                        paramName = "#" + paramName;
                    }
                    Object value = cachedExpressionEvaluator.parseExpression(paramName, elementKey, evaluationContext);
                    matcher.appendReplacement(bufferStr, Objects.isNull(value) ? "" : value.toString());
                }
                matcher.appendTail(bufferStr);
                map.put(template, bufferStr.toString());
            }
        }
        return map;
    }
}
